package com.red.security;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.red.result.Result;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @author: WuShaoJun
 * @create: 2022-09-08 16:05
 **/
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //从requestHeader获取Token
        String header = request.getHeader("Authorization");
        //判断请求中是否携带Token，并且是以”Bearer “开头
        if(ObjectUtil.isEmpty(header) || !header.startsWith("Bearer ")) {
            chain.doFilter(request, response);
            return;
        }
        UsernamePasswordAuthenticationToken authenticationToken
                = getAuthentication(header,response);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request,response);
    }

    public UsernamePasswordAuthenticationToken getAuthentication(
            String header,HttpServletResponse response
    ) throws IOException {
        try {
            //解析Token
            Claims claims =
                    Jwts.parser()//.setSigningKey("security")
                            .parseClaimsJwt(header.replace("Bearer ", ""))
                            .getBody();

            //获取Token的签发时间
            long issuedAt = claims.getIssuedAt().getTime();
            //获取Token的过期时间
            long expiration = claims.getExpiration().getTime();
            //获取当前时间
            long currentTimeMillis = System.currentTimeMillis();
            //如果当前时间小于过期时间，则代表Token没有过期
            if (currentTimeMillis < expiration) {
                //刷新Token
                //获取当前时间作为令牌的颁发时间
                Calendar calendar = Calendar.getInstance();
                Date now = calendar.getTime();
                calendar.setTime(now);
                //过期时间
                calendar.add(Calendar.HOUR, 24);
                //生成Token
                String refreshToken = Jwts.builder()
                        .setSubject(claims.getSubject())
                        .setIssuedAt(now) //设置颁发时间
                        .setExpiration(calendar.getTime()) //设置过期时间
                        //.signWith(SignatureAlgorithm.ES512, "security")
                        .compact();
                //返回新的Token，由前端刷新替换
                response.addHeader("refreshToken", refreshToken);
            }
            String user = claims.getSubject();
            //截取出用户的角色
            List<GrantedAuthority> authorities = new ArrayList<>();
            if(user.lastIndexOf("-") != user.length() - 1){
                String[] roleList = user.split("-")[1]
                        .replace("[","")
                        .replace("]","")
                        .split(",");
                //把角色转为权限
                for (String s : roleList) {
                    authorities.add(new SimpleGrantedAuthority(s.trim()));
                }
            }
            return new UsernamePasswordAuthenticationToken(
                    user,
                    null,
                    authorities
            );
        }catch (ExpiredJwtException e){
            throw e;
        }catch (Exception e){
            throw e;
        }
    }
}
